#include "stdlib.hpp"

#include <map>
#include <sys/select.h>

#include <purewell/types.hpp>
#include <purewell/file.hpp>
#include <purewell/pairfile.hpp>
#include <purewell/iopoll.hpp>
#include "iopollselect.hpp"

namespace purewell
{

PWIOPollSelect::PWIOPollSelect()
{
}

bool
PWIOPollSelect::initialize(void*)
{
	FD_ZERO(&m_rset);
	FD_ZERO(&m_wset);
	FD_ZERO(&m_eset);

	return true;
}

void
PWIOPollSelect::destroy(void)
{
	m_fds.clear();
}

bool
PWIOPollSelect::add(PWPairFile* pitem, size_t idx, int event)
{
	const int fd(pitem->getHandle(idx));

	if ( event&PWIO_R ) FD_SET(fd,&m_rset);
	if ( event&PWIO_W ) FD_SET(fd,&m_wset);
	if ( event&PWIO_E ) FD_SET(fd,&m_eset);

	m_fds.insert(fdmap_t::value_type(fd, pitem));

	return true;
}

bool
PWIOPollSelect::add(PWFile* pitem, int event)
{
	const int fd(pitem->getHandle());

	if ( event&PWIO_R ) FD_SET(fd,&m_rset);
	if ( event&PWIO_W ) FD_SET(fd,&m_wset);
	if ( event&PWIO_E ) FD_SET(fd,&m_eset);

	m_fds.insert(fdmap_t::value_type(fd, pitem));

	return true;
}

bool
PWIOPollSelect::modify(PWPairFile* pitem, size_t idx, int event)
{
	const int fd(pitem->getHandle(idx));

	if ( event&PWIO_R ) FD_SET(fd,&m_rset);
	else FD_CLR(fd,&m_rset);
	if ( event&PWIO_W ) FD_SET(fd,&m_wset);
	else FD_CLR(fd,&m_wset);
	if ( event&PWIO_E ) FD_SET(fd,&m_eset);
	else FD_CLR(fd,&m_eset);

	return true;
}

bool
PWIOPollSelect::modify(PWFile* pitem, int event)
{
	const int fd(pitem->getHandle());

	if ( event&PWIO_R ) FD_SET(fd,&m_rset);
	else FD_CLR(fd,&m_rset);
	if ( event&PWIO_W ) FD_SET(fd,&m_wset);
	else FD_CLR(fd,&m_wset);
	if ( event&PWIO_E ) FD_SET(fd,&m_eset);
	else FD_CLR(fd,&m_eset);

	return true;
}

bool
PWIOPollSelect::modify(int fd, int event)
{
	if ( event&PWIO_R ) FD_SET(fd,&m_rset);
	else FD_CLR(fd,&m_rset);
	if ( event&PWIO_W ) FD_SET(fd,&m_wset);
	else FD_CLR(fd,&m_wset);
	if ( event&PWIO_E ) FD_SET(fd,&m_eset);
	else FD_CLR(fd,&m_eset);

	return true;
}

bool
PWIOPollSelect::remove(PWPairFile* pitem, size_t idx)
{
	const int fd(pitem->getHandle(idx));

	FD_CLR(fd,&m_rset);
	FD_CLR(fd,&m_wset);
	FD_CLR(fd,&m_eset);

	m_fds.erase(fd);

	return true;
}

bool
PWIOPollSelect::remove(PWFile* pitem)
{
	const int fd(pitem->getHandle());

	FD_CLR(fd,&m_rset);
	FD_CLR(fd,&m_wset);
	FD_CLR(fd,&m_eset);

	m_fds.erase(fd);

	return true;
}

bool
PWIOPollSelect::remove(int fd)
{
	FD_CLR(fd,&m_rset);
	FD_CLR(fd,&m_wset);
	FD_CLR(fd,&m_eset);

	m_fds.erase(fd);

	return true;
}

bool
PWIOPollSelect::dispatch(int msec)
{
	fd_set rset(m_rset); 
	fd_set wset(m_wset); 
	fd_set eset(m_eset); 

	struct timeval tv;
	tv.tv_sec = msec / 1000;
	tv.tv_usec = msec % 1000;

	const int res(select(FD_SETSIZE,
		&rset, &wset, &eset,
		&tv) );

	if ( -1 == res && errno != EAGAIN )
	{
		_PWTRACE("%s", strerror(errno));
		return false;
	}

	int event(0);
	int fd(-1);
	PWFile* file(NULL);
	fdmap_itr ib(m_fds.begin());
	fdmap_itr ie(m_fds.end());

	while ( ib != ie )
	{
		fd = ib->first;
		file = ib->second;
		event = 0;

		if ( FD_ISSET(fd, &rset) ) event |= PWIO_R;
		if ( FD_ISSET(fd, &wset) ) event |= PWIO_W;
		if ( FD_ISSET(fd, &eset) ) event |= PWIO_E;

		++ib;
		if ( event )
		{
			file->onEvent(this, fd, event);
		}
	}

	return true;
}

const char*
PWIOPollSelect::getType(void) const
{
	static const char type[] = "select";
	return type;
}

};//namespace purewell

